/*
 * Comunicador.cpp
 *
 *  PEREIRA, FERNANDO ROQUE
 *  91610
 */

#include "Comunicador.h"
#include "MyException.h"

Comunicador::Comunicador(){
	this->socket = new SocketCliente();
	this->ClaveSimetrica="default";
}

Comunicador::Comunicador(const int sockFd){
	this->socket = new SocketCliente(sockFd);
	this->ClaveSimetrica="default";
}

Comunicador::~Comunicador() {
	this->socket->shutDownSocket();
	this->socket->cerrarSocket();
	delete this->socket;
}

void Comunicador::setClaveSimetrica(const std::string& SimK){
	this->ClaveSimetrica= SimK;
}

int Comunicador::conectar(int numeroPuerto, const char* ip) {
	// Lleno estructura para saber a donde enviar y recibir mensajes
	this->socket->setAddr_in(numeroPuerto, ip);
	return (this->socket->connectSocket());
}

int Comunicador::enviarMensaje(std::string &m){

	std::string longm;
	std::stringstream conversor;
	conversor << m.size();
	conversor >> longm;

	//envio el hmac para que se verifique del otro lado
	HMAC hmacc(this->ClaveSimetrica,(unsigned char*)m.c_str(),m.size());

	//el total de lo que voy a enviar
	std::string message = longm + " " + m + hmacc.getHmac();
	
	bool REC_FAIL =  1;
	
	while ( REC_FAIL )
	{
		// Envia la longitud del mensaje que se esta por enviar.
		size_t n = message.size(), nEnviado = 0, nAux=0;
		int j = this->socket->sendMessage(&n, sizeof(size_t));
		if (j < 0) throw MyException("Hubo un error con la comunicacion (al enviar)\n");
		if ( j == 0 ) 
		{
			return 0;
		}
		const char * buffer = message.c_str();
		//std::cout << "long enviada" << std::endl;
		// Se intenta enviar hasta que se haya enviado la totalidad del mensaje.
		while (nEnviado < n)
		{
			nAux = this->socket->sendMessage(buffer + nEnviado, n - nEnviado);
			if (nAux < 0){throw MyException("Hubo un error con la comunicacion (al enviar)\n");}
			if ( nAux == 0 ) 
			{
				return 0;
			}
			else nEnviado += nAux;
		}
		int ret = this->socket->receiveMessage(&REC_FAIL,sizeof(bool));
		if (ret < 0) throw MyException("Hubo un error con la comunicacion (al enviar)\n");
	}
	//std::cout << "finished" << std::endl;
	return 1;
}

int Comunicador::recibirMensaje(std::string& buffer) {

	bool fail = true;
	while (fail == true){
		// Recibo la longitud del mensaje que tengo que leer.
		size_t n;
		int j = this->socket->receiveMessage(&n, sizeof(size_t));
		if (j < 0) throw MyException("Hubo un error con la comunicacion (al recibir)\n");
		//if ( j == 0 ) throw ClientDisconnectedException();
		if ( j == 0 ){
			return 0;
		}
		// Inicializo las variables necesarias.
		buffer.clear();
		char bufferInt[256];
		memset(bufferInt, 0, 256);
		size_t nLeido = 0, nAux;

		// Recibo hasta tener la cantidad necesaria de caracteres.
		while (nLeido < n){
			int p = ((256 < (n - nLeido)) ? 255 : n - nLeido);
			nAux = this->socket->receiveMessage(bufferInt, p);
			if (nAux < 0) throw MyException("Hubo un error con la comunicacion (al recibir)\n");
			//if (nAux == 0) throw ClientDisconnectedException();
			if ( nAux == 0)
			{
				return 0;
			}
			else nLeido += nAux;
			bufferInt[p] = '\0';
			buffer += bufferInt;
		};
		//std::cout << "RECIBIDO: "<< buffer << std::endl;
		//aca viene la verificacion HMAC
		std::stringstream S(buffer);
		unsigned int LongData;
		S >> LongData;
		S.ignore(1);
		char* mensaje = new char[LongData+1];
		mensaje[LongData]='\0';
		S.read(mensaje,LongData);
		//std::cout << "msj recibido: "<< mensaje << std::endl;
		//std::cout << "long msj: "<< LongData << std::endl;
		std::string HmacCalc =
		HMAC(this->ClaveSimetrica,(unsigned char*)mensaje,LongData).getHmac();

		char* HMacVerificacion = new char[32+1] ;
		HMacVerificacion[32]='\0';
		S.read(HMacVerificacion,32);
		if ( HmacCalc != HMacVerificacion )
		{
			/*std::string error ="Integridad del msj comprometida! HMAC-MD5 dio distinto\n";
			error = error + "calculado localmente: "+ HmacCalc
							+ "\n" + "msj recibido: " +  mensaje + "\n" + "Key: "+this->ClaveSimetrica
							+ "\n != \n" + "recibido: "+HMacVerificacion;*/
			bool fail = 1;
			nAux = this->socket->sendMessage(&fail, sizeof(fail));
			//throw MyException(error);
		} else {
			bool ok = 0;
			nAux = this->socket->sendMessage(&ok, sizeof(ok));
			fail = false;
			buffer = mensaje;
		}
		delete [] mensaje;
		delete [] HMacVerificacion;
	}
	return 1;
}

void Comunicador::cerrarComunicacion() {
	this->socket->shutDownSocket();
	this->socket->cerrarSocket();
	/*if ( this->socket != NULL )
	{
	    delete (this->socket);
	    this->socket=NULL;
	}*/
}
